package gazzera.node.core.plugins.upload;

import gazzera.gui.*;
import gazzera.node.core.*;
import gazzera.node.core.messages.*;
import gazzera.node.core.peer.*;
import gazzera.node.core.plugins.download.*;
import gazzera.node.core.plugins.shareplugin.*;
import gazzera.node.core.plugins.shareplugin.db.*;
import gazzera.utils.*;

import java.io.*;
import java.util.*;


public class UploadPlugin implements Plugin
{
	private Set<Node> nodeReady = new HashSet<Node>();
	private Set<Node> nodeUploading = new HashSet<Node>();
	private SharePlugin sharePlugin = null;
	private UploadQueue queue = new UploadQueue();
	
    
	@Override
	public void command(GuiMessage msg) throws Exception
	{
		
	}

	@Override
	public void dataPumpTerminated(Node node, DataPumpSource src) throws Exception
	{
		if ( src instanceof UploadDataPump)
		{
		    nodeUploading.remove(node);
		    checkStartUpload();
			
		}

	}

	@Override
	public void finish() throws Exception
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void init(ICore core) throws Exception
	{
		//this.core = core;
		sharePlugin = core.getPlugin(SharePlugin.class);

	}

	@Override
	public void message(Node node, Message msg) throws Exception
	{
		if(msg instanceof ChunkSearch)
		{
			ChunkSearch cs = (ChunkSearch)msg;
			
			replyToChunkSearch(node, cs);
		}
		else if(msg instanceof Enqueue)
		{
			Enqueue e = (Enqueue)msg;
			
			replyToEnqueue(node, e);
		}
		else if(msg instanceof StartToSend)
		{
		    StartToSend e = (StartToSend)msg;
			
		    replyToStartToSend(node, e);
		}
		else if(msg instanceof NotInterested)
		{
			// NotInterested e = (NotInterested)msg;
			// TODO should use the HASH
		    nodeReady.remove(node);
		    // TODO should interrupt datapumps!
		    checkStartUpload();
		}

	}

	@Override
	public void nodeConnected(Node node) throws Exception
	{

	}

	@Override
	public void nodeDisconnected(Node node) throws Exception
	{
		 nodeReady.remove(node);
		 queue.remove(node);
		 checkStartUpload();
	}

	@Override
	public void start() throws Exception
	{

	}

	@Override
	public void taskTerminated(Task task, boolean interrupted) throws Exception
	{

	}

	
	private void replyToStartToSend(Node node, StartToSend e)
	{
		if (nodeReady.contains(node))
		{
			nodeReady.remove(node);
			nodeUploading.add(node);
			FileInfo f = sharePlugin.find(e.fileId);

			long pos = (e.chunk * Download.CHUNK_SIZE) + e.offset;
			int toSend = f.getChunkSize(e.chunk) - e.offset;
			DataPumpSource u = new UploadDataPump(new File(f.getFullName()), pos, toSend);
			
			node.enqueue(u);
		} 
		else
		{
			System.out.println("StartToSend but node is not in nodeReady");
		}
	}
	private void replyToEnqueue(Node node, Enqueue e)
	{
		// ensure we have it 
		FileInfo f = sharePlugin.find(e.fileId);
		
		if( f != null)
		{
			if(e.fileId instanceof UserFileID)
			{
				// check if the file got hashed in the meanwhile
				if(!f.hash.equals(Hash.EMPTY))
				{
					// it got hashed, let's notify the user
					node.enqueue(new FileHash((UserFileID)e.fileId, f.hash));
					// to reply with the new fileid
					e.fileId = f.hash;
				}
			}
			
		    queue.add(node, e.fileId);
		    checkStartUpload();
		}
	}

	private void checkStartUpload()
	{
	    if((nodeUploading.size() + nodeReady.size()) > 5) return;
	    
	    Pair<Node, FileID> p  = queue.poll(nodeUploading, nodeReady);
	    if(p==null) return;
	    
	    p.a.enqueue(new ReadyToSend(p.b, new BitSet(0)));
	    nodeReady.add(p.a);

	}
	private void replyToChunkSearch(Node node, ChunkSearch cs)
	{
		FileInfo f = sharePlugin.find(cs.fileId);
		if( f != null)
		{
			File file = new File(f.getFullName()).getAbsoluteFile();
			// check if it's still as we know it, and also check to see if it's currently shared,
			// as our DB could contain old FileDesc not shared anymore.
			boolean exists = file.exists();
			boolean intact = (file.lastModified() == f.timestamp);
			boolean sameSize = (file.length() == f.size);
			boolean shared = sharePlugin.getShares().isShared(file);
			System.out.println("CHUNK SEARCH RESPONSE for "+file+" "+exists+" "+intact+" "+sameSize+" "+shared);
			if( exists && intact && sameSize && shared)
			{
				System.out.println("Found a match! "+f.getFullName());
				node.enqueue(new ChunksAvaiable(cs.fileId, new BitSet(0)));
			}
		
		}
	}

	
}
